﻿using System;
using System.Windows.Controls;
using System.Reflection;
using System.Windows.Input;
using System.Windows;
using Sparkle.MVVM.ActionResults;

namespace Sparkle.MVVM
{
    public class CommandBehaviorBase<T>
        where T : Control
    {
        private ICommand _command;
        private object _commandParameter;
        private readonly WeakReference _targetObject;
        private readonly EventHandler _commandCanExecuteChangedHandler;

        private string _action = string.Empty;


        /// <summary>
        /// Constructor specifying the target object.
        /// </summary>
        /// <param name="targetObject">The target object the behaviour is attached to.</param>
        public CommandBehaviorBase(T targetObject)
        {
            _targetObject = new WeakReference(targetObject);
            _commandCanExecuteChangedHandler = new EventHandler(CommandCanExecuteChanged);
        }

        /// <summary>
        /// Corresponding command to be execute and monitored for <see cref="ICommand.CanExecuteChanged"/>
        /// </summary>
        public ICommand Command
        {
            get { return _command; }
            set
            {
                if (_command != null)
                {
                    _command.CanExecuteChanged -= _commandCanExecuteChangedHandler;
                }

                _command = value;
                if (_command != null)
                {
                    _command.CanExecuteChanged += _commandCanExecuteChangedHandler;
                    UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// The parameter to supply the command during execution
        /// </summary>
        public object CommandParameter
        {
            get { return _commandParameter; }
            set
            {
                if (_commandParameter != value)
                {
                    _commandParameter = value;
                    UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// Object to which this behavior is attached.
        /// </summary>
        protected T TargetObject
        {
            get
            {
                return _targetObject.Target as T;
            }
        }



        public string Action
        {
            get { return _action; }
            set { _action = value; }
        }

        /// <summary>
        /// Updates the target object's IsEnabled property based on the commands ability to execute.
        /// </summary>
        protected virtual void UpdateEnabledState()
        {
            if (TargetObject == null)
            {
                Command = null;
                CommandParameter = null;
            }
            else if (Command != null)
            {
                TargetObject.IsEnabled = Command.CanExecute(CommandParameter);
            }
        }

        private void CommandCanExecuteChanged(object sender, EventArgs e)
        {
            UpdateEnabledState();
        }

        /// <summary>
        /// Executes the command, if it's set, providing the <see cref="CommandParameter"/>
        /// </summary>
        protected virtual void ExecuteCommand()
        {
            if (Command != null)
            {
                Command.Execute(CommandParameter);
            }

            if (!string.IsNullOrEmpty(Action))
            {
                object controlller = getController(TargetObject);

                if (controlller != null)
                {
                    MethodInfo actionInfo = controlller.GetType().GetMethod(Action);

                    Object retObject = actionInfo.Invoke(controlller, null);

                    if (retObject != null && retObject is IActionResult)
                    {
                        (retObject as IActionResult).Execute();
                    }
                }
            }
        }

        #region private methods

        private static Object getController(FrameworkElement control)
        {
            object retControlller = null;
            if (control != null && control.DataContext !=null)
            {
                retControlller = control.DataContext;
            }
            else
            {
                if (control != null && control.Parent != null && control.Parent is FrameworkElement)
                {
                    return getController(control.Parent as FrameworkElement);//VisualTreeHelper.GetParent(control)
                }
            }

            return retControlller;
        }

        #endregion
    }

}

